Nodeのバージョンを上げたらnode-sassがエラーを吐いたのでコードリーディングしてみた
起きた事象
フロントエンドのビルド環境としてNode v8で動いていたプロジェクトをNode v10にアップデートしました。その結果、次のような Node Sass could not find a binding for your current environment というエラーメッセージを吐いて落ちました。
どうやら使用していたモジュールのnode-sassでエラーが起きたようです。
Error: Missing binding /path/to/project/node_modules/node-sass/vendor/darwin-x64-64/binding.node Node Sass could not find a binding for your current environment: OS X 64-bit with Node.js 10.x Found bindings for the following environments: - OS X 64-bit with Node.js 8.x This usually happens because your environment has changed since running `npm install`. Run `npm rebuild node-sass` to download the binding for your current environment. at module.exports (/path/to/project/node_modules/node-sass/lib/binding.js:15:13) at Object.<anonymous> (/path/to/project/node_modules/node-sass/lib/index.js:14:35) at Module._compile (internal/modules/cjs/loader.js:778:30)
解決方法
エラーメッセージを読むと書いてありますが、次のコマンドをたたけば動きました。
$ npm rebuild node-sass
おしまい。
始めに
こんにちは。サービスグループの武田です。
今回遭遇したエラーは上記のとおり、コマンド一発たたけば解決しました。ただこれで終わってしまうのもなんですので、もう少し原因周りを調べてみました。
検証環境
次の環境で検証をしました。Node v8でnode-sassをインストールし、そのあとNode v10に切り替えると再現するはずです。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.5 BuildVersion: 18F132 $ node -v v8.16.1 # switch node version $ node -v v10.16.3
エラーメッセージから探る原因
エラーはnode_modules/node-sass/vendor/darwin-x64-64/binding.node
が見つからないというメッセージでした。ディレクトリを確認してみます。
$ tree node_modules/node-sass/vendor/ node_modules/node-sass/vendor/ └── darwin-x64-57 └── binding.node 1 directory, 1 file
たしかにdarwin-x64-64
ではなくdarwin-x64-57
でした。darwin-x64はわかります。OSプラットフォームとアーキテクチャです。では最後の57や64とはなんでしょうか。わからないならソースを見よう、ということで確認しました。
最初に掲載したスタックトレースから、最後に実行されたのはnode-sass/lib/binding.js:15
ということがわかります。
if (!ext.hasBinary(ext.getBinaryPath())) { if (!ext.isSupportedEnvironment()) { throw new Error(errors.unsupportedEnvironment()); } else { throw new Error(errors.missingBinary()); } }
node-sass/binding.js at v4.12.0 · sass/node-sass
15行目は単にErrorを投げているだけですし、12行目のif文はエラー種別の分岐をしているだけです。ということは直前の!ext.hasBinary(ext.getBinaryPath())
がtrueを返していることが原因のようです。このgetBinaryPath()
が怪しいですね。次にこれを確認してみましょう(ちなみにhasBinary()
はファイルの存在チェックをしているだけです)。
function getBinaryPath() { var binaryPath; if (getArgument('--sass-binary-path')) { binaryPath = getArgument('--sass-binary-path'); } else if (process.env.SASS_BINARY_PATH) { binaryPath = process.env.SASS_BINARY_PATH; } else if (process.env.npm_config_sass_binary_path) { binaryPath = process.env.npm_config_sass_binary_path; } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryPath) { binaryPath = pkg.nodeSassConfig.binaryPath; } else { binaryPath = path.join(getBinaryDir(), getBinaryName().replace(/_(?=binding\.node)/, '/')); }
node-sass/extensions.js at v4.12.0 · sass/node-sass
注目すべきは308行目のgetBinaryName().replace(/_(?=binding\.node)/, '/')
です。replaceメソッドはxxx_binding.node
をxxx/binding.node
に置換している部分で、かなりそれっぽい処理をしています。ではgetBinaryName()
が何をしているか、ですが、次のような処理をこの関数内で行っています。
binaryName = [ platform, '-', process.arch, '-', process.versions.modules ].join(''); } return [binaryName, 'binding.node'].join('_'); }
platform
、process.arch
、process.versions.modules
を-
で結合し、最後に_binding.node
を結合しています。どうやら57や64という数字はここから来ていたようです。
process.versions.modulesとは
公式ドキュメントを参照します。
Process | Node.js v10.16.3 Documentation
ドキュメントによると、process.versions.modules
は current ABI version を示す値のようです。ABIとはApplication Binary Interfaceのことで、このバージョンによってC++アドオンを再コンパイルすることなくロード可能かをチェックするようです。
ではこのバージョンがどこで確認できるかというと、リリース一覧のページにありました *1。
NODE_MODULE_VERSION列がそうです。Node.js 10.16.3は64、Node.js 8.16.1は57ということが確認でき、今回の検証環境と一致します。
rebuildの結果を確認
さて、冒頭の解決策でも記載したrebuildの結果をあらためて確認してみましょう。
$ node -v v10.16.3 $ node -p 'process.versions.modules' 64 $ npm rebuild node-sass $ tree node_modules/node-sass/vendor/ node_modules/node-sass/vendor/ ├── darwin-x64-57 │ └── binding.node └── darwin-x64-64 └── binding.node 2 directories, 2 files
なるほど、rebuildすることで対応したnode-sassのバイナリが生成されていることが確認できました。
まとめ
node-sassはプラットフォームおよびNodeのバージョンに合わせてバイナリを生成します。そのため、環境が変われば新しい環境用にバイナリを用意する必要があります。そのためのrebuildだったわけですね。
Nodeの環境について詳しい方なら常識だったでしょうか。また少し理解が深まりました。
おまけ
同じくnode-sassのエラーで Error: Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (64) の場合はrebuildでは解決しません。使用しているモジュールのバージョンがサポートしている環境を確認し、アップデートで解決するようであればアップデートしましょう。
脚注
- ちなみにこのURLもソースコードに書いてありました。node/node_version.h at v10.16.3 · nodejs/node ↩